
// 战斗在前端第一线，封装一下自己平常需要用到的各种代码
//  ps： 大部分从网上找的


/**
* @desc 判断两个数组是否相等
* @param {Array} arr1
* @param {Array} arr2
* @return {Boolean}
*/

function xxw_arrayEqual(arr1, arr2) {
    if (arr1 == arr2) return true;

    if (arr1.length != arr2.length) return false;

    for (var i = 0; i < arr1.length; ++i) {
        if (arr1[i] !== arr2[i]) return false;
    }
    return true;

}

/**
 * 对Date的扩展，将 Date 转化为指定格式的String
 * 月(M)、日(d)、12小时(h)、24小时(H)、分(m)、秒(s)、周(E)、季度(q) 可以用 1-2 个占位符
 * 年(y)可以用 1-4 个占位符，毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
 * eg:
 * (new Date()).format("yyyy-MM-dd hh:mm:ss.S") ==>     2017-12-14 02:32:10.729
 * (new Date()).format("yyyyMMddhhmmss)   ==>           20171214023210
 * (new Date()).format("yyyy-MM-dd E HH:mm:ss") ==>     2009-03-10 二 20:09:04
 * (new Date()).format("yyyy-MM-dd EE hh:mm:ss") ==>    2009-03-10 周二 08:09:04
 * (new Date()).format("yyyy-MM-dd EEE hh:mm:ss") ==>   2009-03-10 星期二 08:09:04
 * (new Date()).format("yyyy-M-d h:m:s.S") ==>          2006-7-2 8:9:4.18
 */
Date.prototype.xxw_format = function (fmt) {
    var o = {
        "M+": this.getMonth() + 1, //月份
        "d+": this.getDate(), //日
        "h+": this.getHours() % 12 == 0 ? 12 : this.getHours() % 12, //小时
        "H+": this.getHours(), //小时
        "m+": this.getMinutes(), //分
        "s+": this.getSeconds(), //秒
        "q+": Math.floor((this.getMonth() + 3) / 3), //季度
        "S": this.getMilliseconds() //毫秒
    };
    var week = {
        "0": "/u65e5",
        "1": "/u4e00",
        "2": "/u4e8c",
        "3": "/u4e09",
        "4": "/u56db",
        "5": "/u4e94",
        "6": "/u516d"
    };
    if (/(y+)/.test(fmt)) {
        fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
    }
    if (/(E+)/.test(fmt)) {
        fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? "/u661f/u671f" : "/u5468") : "") + week[this.getDay() + ""]);
    }
    for (var k in o) {
        if (new RegExp("(" + k + ")").test(fmt)) {
            fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
        }
    }
    return fmt;
}
/**
* @desc 格式化${startTime} 距现在的已过时间
* @param {Date} startTime
* @return {String}
*/
function xxw_formatPassTime(startTime) {
    var currentTime = Date.parse(new Date()),
        time = currentTime - startTime,
        day = parseInt(time / (1000 * 60 * 60 * 24)),
        hour = parseInt(time / (1000 * 60 * 60)),
        min = parseInt(time / (1000 * 60)),
        month = parseInt(day / 30),
        year = parseInt(month / 12);
    if (year) return year + "年前"
    if (month) return month + "个月前"
    if (day) return day + "天前"
    if (hour) return hour + "小时前"
    if (min) return min + "分钟前"
    else return '刚刚'
}
/**
*
* @desc 格式化现在距${endTime}的剩余时间
* @param {Date} endTime
* @return {String}
*/
function xxw_formatRemainTime(endTime) {
    var startDate = new Date(); //开始时间
    var endDate = new Date(endTime); //结束时间
    var t = endDate.getTime() - startDate.getTime(); //时间差
    var d = 0,
        h = 0,
        m = 0,
        s = 0;
    if (t >= 0) {
        d = Math.floor(t / 1000 / 3600 / 24);
        h = Math.floor(t / 1000 / 60 / 60 % 24);
        m = Math.floor(t / 1000 / 60 % 60);
        s = Math.floor(t / 1000 % 60);
    }
    return d + "天 " + h + "小时 " + m + "分钟 " + s + "秒";
}
/**
* @desc 判断元素是否有某个class
* @param {HTMLElement} ele
* @param {String} cls
* @return {Boolean}
*/
function xxw_hasClass(ele, cls) {
    return (new RegExp('(\\s|^)' + cls + '(\\s|$)')).test(ele.className);
}


/**
* @desc 获取浏览器类型和版本
* @return {String}
*/
function xxw_getExplore() {
    var sys = {},
        ua = navigator.userAgent.toLowerCase(),
        s;
    (s = ua.match(/rv:([\d.]+)\) like gecko/)) ? sys.ie = s[1] :
        (s = ua.match(/msie ([\d\.]+)/)) ? sys.ie = s[1] :
            (s = ua.match(/edge\/([\d\.]+)/)) ? sys.edge = s[1] :
                (s = ua.match(/firefox\/([\d\.]+)/)) ? sys.firefox = s[1] :
                    (s = ua.match(/(?:opera|opr).([\d\.]+)/)) ? sys.opera = s[1] :
                        (s = ua.match(/chrome\/([\d\.]+)/)) ? sys.chrome = s[1] :
                            (s = ua.match(/version\/([\d\.]+).*safari/)) ? sys.safari = s[1] : 0;
    // 根据关系进行判断
    if (sys.ie) return ('IE: ' + sys.ie)
    if (sys.edge) return ('EDGE: ' + sys.edge)
    if (sys.firefox) return ('Firefox: ' + sys.firefox)
    if (sys.chrome) return ('Chrome: ' + sys.chrome)
    if (sys.opera) return ('Opera: ' + sys.opera)
    if (sys.safari) return ('Safari: ' + sys.safari)
    return 'Unkonwn'
}

/**
* @desc 获取设备的操作类型
* @return {String}
*/
function xxw_getOS() {
    var userAgent = 'navigator' in window && 'userAgent' in navigator && navigator.userAgent.toLowerCase() || '';
    var vendor = 'navigator' in window && 'vendor' in navigator && navigator.vendor.toLowerCase() || '';
    var appVersion = 'navigator' in window && 'appVersion' in navigator && navigator.appVersion.toLowerCase() || '';

    if (/mac/i.test(appVersion)) return 'MacOSX'
    if (/win/i.test(appVersion)) return 'windows'
    if (/linux/i.test(appVersion)) return 'linux'
    if (/iphone/i.test(userAgent) || /ipad/i.test(userAgent) || /ipod/i.test(userAgent)) 'ios'
    if (/android/i.test(userAgent)) return 'android'
    if (/win/i.test(appVersion) && /phone/i.test(userAgent)) return 'windowsPhone'
}

/**
* @desc 获取滚动条距顶部的距离
* @param
* @return {String}
*
*/
function xxw_getScrollTop() {
    return (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
}

/**
*
* @desc 随机生成颜色
* @return {String}
*/function xxw_randomColor() {
    return '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).slice(-6);
}
/**
*
* @desc 生成指定范围随机数
* @param {Number} min
* @param {Number} max
* @return {Number}
*/function xxw_randomNum(min, max) {
    return Math.floor(min + Math.random() * (max - min));
}

/**
*
* @desc 判断是否为邮箱地址
* @param {String} str
* @return {Boolean}
*/function xxw_isEmail(str) {
    return /\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/.test(str);
}
/**
*
* @desc 判断是否为身份证号
* @param {String|Number} str
* @return {Boolean}
*/function xxw_isIdCard(str) {
    return /^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$/.test(str)
}
/**
*
* @desc 判断是否为手机号
* @param {String|Number} str
* @return {Boolean}
*/
function xxw_isPhoneNum(str) {
    return /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/.test(str)
}
/**
*
* @desc 判断是否为URL地址
* @param {String} str
* @return {Boolean}
*/function xxw_isUrl(str) {
    return /[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/i.test(str);
}
/**
*
* @desc 现金额转大写
* @param {Number} n
* @return {String}
*/
function xxw_digitUppercase(n) {
    var fraction = ['角', '分'];
    var digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
    var unit = [['元', '万', '亿'], ['', '拾', '佰', '仟']];
    var head = n < 0 ? '欠' : '';
    n = Math.abs(n);
    var s = '';
    for (var i = 0; i < fraction.length; i++) {
        s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '');
    }
    s = s || '整';
    n = Math.floor(n);
    for (var i = 0; i < unit[0].length && n > 0; i++) {
        var p = '';
        for (var j = 0; j < unit[1].length && n > 0; j++) {
            p = digit[n % 10] + unit[1][j] + p;
            n = Math.floor(n / 10);
        }
        s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s;
    }
    return head + s.replace(/(零.)*零元/, '元')
        .replace(/(零.)+/g, '零')
        .replace(/^整$/, '零元整');
};
/**
 * throttle
 * @desc 函数节流
 *      适用于限制resize 和 scroll 等函数的调用
 * @param {Number} delay 0或者更大的毫秒数， 对于事件回调，大约100或者250 毫秒的延迟最有用
 * @param {Boolean} noTrailing 可选默认false  
 *         如果noTrailing为true，当节流函数被调用，每过`delay`毫秒`callback`也将执行一次。
 *         如果noTrailing为false或者未传入，`callback`将在最后一次调用节流函数后再执行一次.
 *        （延迟`delay`毫秒之后，节流函数没有被调用,内部计数器会复位）
 * @param {Function} callback 延迟毫秒后执行的函数，this上下文和所有参数都是按原样传递的，
 *          执行去节流功能，调用callback
 * @param {Boolean} debounceMode  如果为true clear 在delay 毫秒后执行
 * @return {Function} 新的节流函数
 * 
 */
function xxw_throttle(delay, noTrailing, callback, debounceMode) {
    var timeoutID;
    var lastExec = 0;
    if (typeof noTrailing !== 'boolean') {
        debounceMode = callback;
        callback = noTrailing;
        noTrailing = undefined;
    }

    function wrapper() {
        var self = this;
        var elapsed = Number(new Date()) - lastExec;
        var args = arguments;
        function exec() {
            lastExec = Number(new Date());
            callback.apply(self, args);
        }
        function clear() {
            timeoutID = undefined;
        }
        if (debounceMode && !timeoutID) {
            exec();
        }
        if (timeoutID) {
            clearTimeout(timeoutID);
        }
        if (debounceMode === undefined && elapsed > delay) {
            exec();

        } else if (noTrailing !== true) {
            timeoutID = setTimeout(
                debounceMode ? clear : exec,
                debounceMode === undefined ? delay - elapsed : delay
            );

        }

    }
    return wrapper;
}




/**
 * debounce 
 * @desc 函数防抖 保证在多少毫秒内不在被触发，只会执行一次，
 * 要么在第一次调用return 的防抖函数时执行，要么在延迟指定毫秒后调用
 * @example  使用场景： 如在线编辑自动存储时防抖
 * @param {Number} delay  0或者更大的毫秒数。对于事件回调，大约100或者250毫秒的延迟最有用
 * @param {Boolean} atBegin 可选，默认false 
 *                          如果`atBegin`为false或未传入，回调函数则在第一次调用return的防抖函数后延迟指定毫秒调用。
                            如果`atBegin`为true，回调函数则在第一次调用return的防抖函数时直接执行
 * @param {Function} callback 延迟毫秒后执行的函数，this 上下文替换和所有参数都是按原样传递的，
 *                          执行去抖动功能是，调用callback 
 * @return  {Function} 新的防抖函数
 * */

var throttle = require('./throttle');
function debounce(delay, atBegin, callback) {
    return callback === undefined ? throttle(delay, atBegin, false) : throttle(delay, callback, atBegin !== false);
};